/* ili9486.c */
#include "ili9486.h"

#include "fsl_common.h"
#include "fsl_clock.h"
#include "fsl_port.h"
#include "fsl_gpio.h"

struct
{
    uint16_t  width;         //LCD 宽度
    uint16_t  height;        //LCD 高度
    uint16_t  id;            //LCD ID
    uint8_t   dir;           //横屏还是竖屏控制：0，竖屏；1，横屏。
    uint16_t  wramcmd;       //开始写gram指令
    uint16_t  setxcmd;       //设置x坐标指令
    uint16_t  setycmd;       //设置y坐标指令
} lcd_info;

/*
* LCD_D0 : PTA13
* LCD_D1 : PTD5
* LCD_D2 : PTD4
* LCD_D3 : PTA12
* LCD_D4 : PTA4
* LCD_D5 : PTA5
* LCD_D6 : PTC8
* LCD_D7 : PTC9
* LCD_RST: PTC2
* LCD_CS : PTB3
* LCD_RS : PTB2
* LCD_WR : PTB1
* LCD_RD : PTB0
*/
void ili9486_init_hardware(void)
{
    CLOCK_EnableClock(kCLOCK_PortA);
    CLOCK_EnableClock(kCLOCK_PortB);
    CLOCK_EnableClock(kCLOCK_PortC);
    CLOCK_EnableClock(kCLOCK_PortD);

    PORT_SetPinMux(PORTA, 13u, kPORT_MuxAsGpio); /* LCD_D0 : PTA13 */
    PORT_SetPinMux(PORTD,  5u, kPORT_MuxAsGpio); /* LCD_D1 : PTD5  */
    PORT_SetPinMux(PORTD,  4u, kPORT_MuxAsGpio); /* LCD_D2 : PTD4  */
    PORT_SetPinMux(PORTA, 12u, kPORT_MuxAsGpio); /* LCD_D3 : PTA12 */
    PORT_SetPinMux(PORTA,  4u, kPORT_MuxAsGpio); /* LCD_D4 : PTA4  */
    PORT_SetPinMux(PORTA,  5u, kPORT_MuxAsGpio); /* LCD_D5 : PTA5  */
    PORT_SetPinMux(PORTC,  8u, kPORT_MuxAsGpio); /* LCD_D6 : PTC8  */
    PORT_SetPinMux(PORTC,  9u, kPORT_MuxAsGpio); /* LCD_D7 : PTC9  */
    PORT_SetPinMux(PORTC,  2u, kPORT_MuxAsGpio); /* LCD_RST: PTC2  */
    PORT_SetPinMux(PORTB,  3u, kPORT_MuxAsGpio); /* LCD_CS : PTB3  */
    PORT_SetPinMux(PORTB,  2u, kPORT_MuxAsGpio); /* LCD_RS : PTB2  */
    PORT_SetPinMux(PORTB,  1u, kPORT_MuxAsGpio); /* LCD_WR : PTB1  */
    PORT_SetPinMux(PORTB,  0u, kPORT_MuxAsGpio); /* LCD_RD : PTB0  */

    gpio_pin_config_t gpio_pin_config;
    gpio_pin_config.pinDirection = kGPIO_DigitalOutput;
    gpio_pin_config.outputLogic  = 1u;
    GPIO_PinInit(GPIOA, 13u, &gpio_pin_config); /* LCD_D0 : PTA13 */
    GPIO_PinInit(GPIOD,  5u, &gpio_pin_config); /* LCD_D1 : PTD5  */
    GPIO_PinInit(GPIOD,  4u, &gpio_pin_config); /* LCD_D2 : PTD4  */
    GPIO_PinInit(GPIOA, 12u, &gpio_pin_config); /* LCD_D3 : PTA12 */
    GPIO_PinInit(GPIOA,  4u, &gpio_pin_config); /* LCD_D4 : PTA4  */
    GPIO_PinInit(GPIOA,  5u, &gpio_pin_config); /* LCD_D5 : PTA5  */
    GPIO_PinInit(GPIOC,  8u, &gpio_pin_config); /* LCD_D6 : PTC8  */
    GPIO_PinInit(GPIOC,  9u, &gpio_pin_config); /* LCD_D7 : PTC9  */
    GPIO_PinInit(GPIOC,  2u, &gpio_pin_config); /* LCD_RST: PTC2  */
    GPIO_PinInit(GPIOB,  3u, &gpio_pin_config); /* LCD_CS : PTB3  */
    GPIO_PinInit(GPIOB,  2u, &gpio_pin_config); /* LCD_RS : PTB2  */
    GPIO_PinInit(GPIOB,  1u, &gpio_pin_config); /* LCD_WR : PTB1  */
    GPIO_PinInit(GPIOB,  0u, &gpio_pin_config); /* LCD_RD : PTB0  */
}

static void ili9486_delay_ms(uint8_t t)
{
    for (uint8_t i = 0u; i < t; i++)
    {
        for (uint32_t j = 0u; j < 0xfff; j++)
        {
            asm("nop");
        }
    }
}

static void ili9486_hw_write_8b_bus(uint8_t dat8)
{
#if 1
    GPIO_WritePinOutput(GPIOA, 13u, (0u != (dat8 & (1u << 0u))) ); /* LCD_D0 : PTA13 */
    GPIO_WritePinOutput(GPIOD,  5u, (0u != (dat8 & (1u << 1u))) ); /* LCD_D1 : PTD5  */
    GPIO_WritePinOutput(GPIOD,  4u, (0u != (dat8 & (1u << 2u))) ); /* LCD_D2 : PTD4  */
    GPIO_WritePinOutput(GPIOA, 12u, (0u != (dat8 & (1u << 3u))) ); /* LCD_D3 : PTA12 */
    GPIO_WritePinOutput(GPIOA,  4u, (0u != (dat8 & (1u << 4u))) ); /* LCD_D4 : PTA4  */
    GPIO_WritePinOutput(GPIOA,  5u, (0u != (dat8 & (1u << 5u))) ); /* LCD_D5 : PTA5  */
    GPIO_WritePinOutput(GPIOC,  8u, (0u != (dat8 & (1u << 6u))) ); /* LCD_D6 : PTC8  */
    GPIO_WritePinOutput(GPIOC,  9u, (0u != (dat8 & (1u << 7u))) ); /* LCD_D7 : PTC9  */
#else
    GPIO_WritePinOutput(GPIOA, 13u, (0u != (dat8 & (1u << 7u))) ); /* LCD_D0 : PTA13 */
    GPIO_WritePinOutput(GPIOD,  5u, (0u != (dat8 & (1u << 6u))) ); /* LCD_D1 : PTD5  */
    GPIO_WritePinOutput(GPIOD,  4u, (0u != (dat8 & (1u << 5u))) ); /* LCD_D2 : PTD4  */
    GPIO_WritePinOutput(GPIOA, 12u, (0u != (dat8 & (1u << 4u))) ); /* LCD_D3 : PTA12 */
    GPIO_WritePinOutput(GPIOA,  4u, (0u != (dat8 & (1u << 3u))) ); /* LCD_D4 : PTA4  */
    GPIO_WritePinOutput(GPIOA,  5u, (0u != (dat8 & (1u << 2u))) ); /* LCD_D5 : PTA5  */
    GPIO_WritePinOutput(GPIOC,  8u, (0u != (dat8 & (1u << 1u))) ); /* LCD_D6 : PTC8  */
    GPIO_WritePinOutput(GPIOC,  9u, (0u != (dat8 & (1u << 0u))) ); /* LCD_D7 : PTC9  */
#endif
}

static void ili9486_hw_write_8b_dat(uint8_t dat8)
{
    GPIO_WritePinOutput(GPIOB, 2u, 1u); /* LCD_RS : PTB2  */ /* write data. */
    GPIO_WritePinOutput(GPIOB, 3u, 0u); /* LCD_CS : PTB3  */

    ili9486_hw_write_8b_bus(dat8);

    GPIO_WritePinOutput(GPIOB, 1u, 0u); /* LCD_WR : PTB1  */
    GPIO_WritePinOutput(GPIOB, 1u, 1u); /* LCD_WR : PTB1  */

    GPIO_WritePinOutput(GPIOB, 3u, 1u); /* LCD_CS : PTB3  */
}

static void ili9486_hw_write_16b_dat(uint16_t dat16)
{
    GPIO_WritePinOutput(GPIOB, 2u, 1u); /* LCD_RS : PTB2  */ /* write data. */
    GPIO_WritePinOutput(GPIOB, 3u, 0u); /* LCD_CS : PTB3  */

    ili9486_hw_write_8b_bus((uint8_t)(0xff & (dat16 >> 8u)) );

    GPIO_WritePinOutput(GPIOB, 1u, 0u); /* LCD_WR : PTB1  */
    GPIO_WritePinOutput(GPIOB, 1u, 1u); /* LCD_WR : PTB1  */

    ili9486_hw_write_8b_bus((uint8_t)(0xff & dat16) );

    GPIO_WritePinOutput(GPIOB, 1u, 0u); /* LCD_WR : PTB1  */
    GPIO_WritePinOutput(GPIOB, 1u, 1u); /* LCD_WR : PTB1  */

    GPIO_WritePinOutput(GPIOB, 3u, 1u); /* LCD_CS : PTB3  */
}

static void ili9486_hw_write_8b_cmd(uint8_t cmd8)
{
    GPIO_WritePinOutput(GPIOB, 2u, 0u); /* LCD_RS : PTB2  */ /* write command. */
    GPIO_WritePinOutput(GPIOB, 3u, 0u); /* LCD_CS : PTB3  */

    ili9486_hw_write_8b_bus(cmd8);

    GPIO_WritePinOutput(GPIOB, 1u, 0u); /* LCD_WR : PTB1  */
    GPIO_WritePinOutput(GPIOB, 1u, 1u); /* LCD_WR : PTB1  */

    GPIO_WritePinOutput(GPIOB, 3u, 1u); /* LCD_CS : PTB3  */
}

/* 在给液晶屏传送RGB数据前，应该发送写GRAM指令 */
void ili9486_write_ram_prepare(void)
{
    //ili9486_hw_write_8b_cmd(lcd_info.wramcmd);
    ili9486_hw_write_8b_cmd(0x2C);
}

#if 0
void ili9486_setup(lcd_portrait_t portrait)
{
    lcd_info.setxcmd=0x2A;
    lcd_info.setycmd=0x2B;
    lcd_info.wramcmd=0x2C;

    if (portrait == eLCD_Portrait_Horizontal)
    {
        lcd_info.dir=1;//横屏
        lcd_info.width=480;
        lcd_info.height=320;
        LCD_WriteReg(0x36, (1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
    }
    else
    {
        lcd_info.dir=0;//竖屏
        lcd_info.width=320;
        lcd_info.height=480;
        LCD_WriteReg(0x36, (1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
    }
}


void ili9486_set_direction(uint8_t direction)
{
    switch (direction)
    {
        case 3:
            lcd_info.width  =480;
            lcd_info.height =320;
            LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
            break;
        case 1:
            lcd_info.width  =480;
            lcd_info.height =320;
            LCD_WriteReg(0x36,(1<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
            break;
        case 2:
            lcd_info.width  =320;
            lcd_info.height =480;
            LCD_WriteReg(0x36,(1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
            break;
        case 0:
            lcd_info.width  =320;
            lcd_info.height =480;
            LCD_WriteReg(0x36,0x0A);
            //LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
            break;
        default:
            break;
    }
}

#endif

void ili9486_reset(void)
{
    GPIO_WritePinOutput(GPIOC, 2u, 0u ); /* LCD_RST: PTC2 */
    ili9486_delay_ms(100u);
    GPIO_WritePinOutput(GPIOC, 2u, 1u ); /* LCD_RST: PTC2 */
    ili9486_delay_ms(50u);
}

void ili9486_init(void)
{
    ili9486_reset();

    //ILI9486初始化
    ili9486_hw_write_8b_cmd(0xF9);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x08);

    ili9486_hw_write_8b_cmd(0xC0);
    ili9486_hw_write_8b_dat(0x19);//VREG1OUT POSITIVE
    ili9486_hw_write_8b_dat(0x1a);//VREG2OUT NEGATIVE

    ili9486_hw_write_8b_cmd(0xC1);
    ili9486_hw_write_8b_dat(0x45);//VGH,VGL    VGH>=14V.
    ili9486_hw_write_8b_dat(0x00);

    ili9486_hw_write_8b_cmd(0xC2);
    ili9486_hw_write_8b_dat(0x33);

    ili9486_hw_write_8b_cmd(0XC5);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x28);//VCM_REG[7:0]. <=0X80.

    ili9486_hw_write_8b_cmd(0xB1);//OSC Freq set.
    ili9486_hw_write_8b_dat(0x90);//0xA0=62HZ,0XB0 =70HZ, <=0XB0.
    ili9486_hw_write_8b_dat(0x11);

    ili9486_hw_write_8b_cmd(0xB4);
    ili9486_hw_write_8b_dat(0x02); //2 DOT FRAME MODE,F<=70HZ.

    ili9486_hw_write_8b_cmd(0xB6);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x42);//0 GS SS SM ISC[3:0];
    ili9486_hw_write_8b_dat(0x3B);

    ili9486_hw_write_8b_cmd(0xB7);
    ili9486_hw_write_8b_dat(0x07);

    ili9486_hw_write_8b_cmd(0xE0);
    ili9486_hw_write_8b_dat(0x1F);
    ili9486_hw_write_8b_dat(0x25);
    ili9486_hw_write_8b_dat(0x22);
    ili9486_hw_write_8b_dat(0x0B);
    ili9486_hw_write_8b_dat(0x06);
    ili9486_hw_write_8b_dat(0x0A);
    ili9486_hw_write_8b_dat(0x4E);
    ili9486_hw_write_8b_dat(0xC6);
    ili9486_hw_write_8b_dat(0x39);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x00);

    ili9486_hw_write_8b_cmd(0XE1);
    ili9486_hw_write_8b_dat(0x1F);
    ili9486_hw_write_8b_dat(0x3F);
    ili9486_hw_write_8b_dat(0x3F);
    ili9486_hw_write_8b_dat(0x0F);
    ili9486_hw_write_8b_dat(0x1F);
    ili9486_hw_write_8b_dat(0x0F);
    ili9486_hw_write_8b_dat(0x46);
    ili9486_hw_write_8b_dat(0x49);
    ili9486_hw_write_8b_dat(0x31);
    ili9486_hw_write_8b_dat(0x05);
    ili9486_hw_write_8b_dat(0x09);
    ili9486_hw_write_8b_dat(0x03);
    ili9486_hw_write_8b_dat(0x1C);
    ili9486_hw_write_8b_dat(0x1A);
    ili9486_hw_write_8b_dat(0x00);

    ili9486_hw_write_8b_cmd(0XF1);
    ili9486_hw_write_8b_dat(0x36);
    ili9486_hw_write_8b_dat(0x04);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x3C);
    ili9486_hw_write_8b_dat(0x0F);
    ili9486_hw_write_8b_dat(0x0F);
    ili9486_hw_write_8b_dat(0xA4);
    ili9486_hw_write_8b_dat(0x02);

    ili9486_hw_write_8b_cmd(0XF2);
    ili9486_hw_write_8b_dat(0x18);
    ili9486_hw_write_8b_dat(0xA3);
    ili9486_hw_write_8b_dat(0x12);
    ili9486_hw_write_8b_dat(0x02);
    ili9486_hw_write_8b_dat(0x32);
    ili9486_hw_write_8b_dat(0x12);
    ili9486_hw_write_8b_dat(0xFF);
    ili9486_hw_write_8b_dat(0x32);
    ili9486_hw_write_8b_dat(0x00);

    ili9486_hw_write_8b_cmd(0XF4);
    ili9486_hw_write_8b_dat(0x40);
    ili9486_hw_write_8b_dat(0x00);
    ili9486_hw_write_8b_dat(0x08);
    ili9486_hw_write_8b_dat(0x91);
    ili9486_hw_write_8b_dat(0x04);

    ili9486_hw_write_8b_cmd(0XF8);
    ili9486_hw_write_8b_dat(0x21);
    ili9486_hw_write_8b_dat(0x04);

    ili9486_hw_write_8b_cmd(0x36);
    ili9486_hw_write_8b_dat(0x48);

    ili9486_hw_write_8b_cmd(0x3A);
    ili9486_hw_write_8b_dat(0x55);

    ili9486_hw_write_8b_cmd(0x11);
    ili9486_delay_ms(120);
    //ili9486_hw_write_8b_cmd(0x21);
    ili9486_hw_write_8b_cmd(0x29);

#if 1
    /* 横屏显示. */
    ili9486_hw_write_8b_cmd(0x36);
    ili9486_hw_write_8b_dat( (1<<3)|(1<<7)|(1<<5) );//BGR==1,MY==1,MX==0,MV==1
#endif

}



void ili9486_set_window(uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1)
{
    ili9486_hw_write_8b_cmd(0x2A);
    ili9486_hw_write_16b_dat(x0);
    ili9486_hw_write_16b_dat(x1);

    ili9486_hw_write_8b_cmd(0x2B);
    ili9486_hw_write_16b_dat(y0);
    ili9486_hw_write_16b_dat(y1);

    ili9486_write_ram_prepare();
}

void ili9486_write_color(uint16_t color)
{
    ili9486_hw_write_16b_dat(color);
}

void ili9486_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
    ili9486_set_window(x, x+1, y, y+1);
    ili9486_hw_write_16b_dat(color);
}

void ili9486_clear_screen(uint16_t color)
{
    uint16_t x, y;
    ili9486_set_window(0, LCD_WIDTH, 0, LCD_HEIGH);
    for (y = 0u; y < LCD_HEIGH; y++)
    {
        for (x = 0u; x < LCD_WIDTH; x++)
        {
            ili9486_hw_write_16b_dat(color);
        }
    }
}

/* EOF. */

